﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Glee.Drawing;
using Microsoft.FSharp.Collections;

namespace CoreReset
{
    using Context = BIT_ARRAY.Bitarray;

    /// <summary>
    /// Class CommandGraphForm is the form used for textual/graphical display of 
    /// Context Graphs / DFRs which are computed for a certain command.
    /// </summary>
    public partial class CommandGraphForm : Form
    {
        CoreResetForm m_mainForm;

        public CommandGraphForm(CoreResetForm mainForm, string text)
        {
            InitializeComponent();
            m_mainForm = mainForm;
            SetText(text);
        }

        public void SetText(string text)
        {
            if (text != null)
            {
                textBoxCommandGraph.Lines = text.Split('\n');
            }
        }

        private void CommandGraphForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            m_mainForm.OnCommandGraphFormClosing();
        }

        private string context2text(FSharpList<int> c)
        {
            string txt = "{ ";
            for (int i = 0; i < c.Length; ++i)
            {
                txt += c[i].ToString() + ((i < c.Length - 1) ? ", " : "");
            }
            txt += " }";
            return txt;
        }

        private Edge AddEdge(Graph g, FSharpList<int> pre, FSharpList<int> post, Tuple<Graph,Graph> dfr)
        {
            string preText = context2text(pre);
            string postText = context2text(post) + "'";
            Edge e = g.AddEdge(preText, postText);
            e.UserData = dfr;
            if (dfr != null)
            {
                dfr.Item1.GraphAttr.LayerDirection = LayerDirection.LR;
                dfr.Item2.GraphAttr.LayerDirection = LayerDirection.LR;
            }
            Node p = e.SourceNode;
            Node q = e.TargetNode;
            p.Attr.Shape = Microsoft.Glee.Drawing.Shape.Plaintext;
            q.Attr.Shape = Microsoft.Glee.Drawing.Shape.Plaintext;
            p.Attr.Color = Microsoft.Glee.Drawing.Color.Red;
            q.Attr.Color = Microsoft.Glee.Drawing.Color.DarkGreen;
            return e;
        }

        private string Pat2Str(BJK.Pattern.Pat pat)
        {
            if (pat == null)
                return "";

            if (pat.IsCmd) {
                BJK.Pattern.Pat.Cmd cmd = pat as BJK.Pattern.Pat.Cmd;
                return cmd.Item.ToString();
            }
            else if (pat.IsSeq)
            {
                BJK.Pattern.Pat.Seq seq = pat as BJK.Pattern.Pat.Seq;
                return Pat2Str(seq.Item1) + ";" + Pat2Str(seq.Item2);
            }
            else if (pat.IsLoop)
            {
                BJK.Pattern.Pat.Loop l = pat as BJK.Pattern.Pat.Loop;
                return l.Item1.ToString() + "[" + Pat2Str(l.Item2) + "]^*";
            }
            else if (pat.IsPow)
            {
                BJK.Pattern.Pat.Pow pow = pat as BJK.Pattern.Pat.Pow;
                return "(" + Pat2Str(pow.Item1) + ")^" + pow.Item2.ToString();
            }

            return "";
        }

        private string Pat2ElaboratedStr(BJK.Pattern.Pat pat, int indent)
        {
            string spaces = "";
            for (int i = 0; i < indent; ++i)
                spaces += " ";
            if (pat == null)
                return spaces;
             if (pat.IsCmd)
            {
                BJK.Pattern.Pat.Cmd cmd = pat as BJK.Pattern.Pat.Cmd;
                string suffix = "";
                int childNum = m_mainForm.ChildNumberUnderBranch(cmd.Item);
                if (childNum > 0)
                {
                    string num =  (childNum == 1) ? "first" : "second";
                    suffix = "    (taking the " + num + " branch)";
                }
                return spaces + m_mainForm.GetCommandText(cmd.Item) + suffix;
            }
            else if (pat.IsSeq)
            {
                BJK.Pattern.Pat.Seq seq = pat as BJK.Pattern.Pat.Seq;
                return Pat2ElaboratedStr(seq.Item1, indent) + ";\n" + Pat2ElaboratedStr(seq.Item2, indent);
            }
            else if (pat.IsLoop)
            {
                BJK.Pattern.Pat.Loop l = pat as BJK.Pattern.Pat.Loop;
                return spaces + "loop (" + l.Item1.ToString() + ") {\n" + Pat2ElaboratedStr(l.Item2,indent+4) + "\n" + spaces + "}";
            }
            else if (pat.IsPow)
            {
                BJK.Pattern.Pat.Pow pow = pat as BJK.Pattern.Pat.Pow;
                return spaces + pow.Item2.ToString() + " * {\n" + Pat2ElaboratedStr(pow.Item1, indent + 4) + "\n" + spaces + "}";
            }

            return spaces;
        }

        private Edge AddVarEdge(Graph g, int src, int tgt, string tag, BJK.Pattern.Pat pat)
        {
            Edge e = g.AddEdge("X" + src.ToString(), tag, "X" + tgt.ToString() + "'");
            e.SourceNode.Attr.Shape = Microsoft.Glee.Drawing.Shape.Plaintext;
            e.TargetNode.Attr.Shape = Microsoft.Glee.Drawing.Shape.Plaintext;
            e.UserData = Pat2ElaboratedStr(pat,0);
            return e;
        }
        static int dEdgeCount = 0;
        private void AddDoubleVarEdge(Graph g, int i, int j, int i1, int j1)
        {
            dEdgeCount++;
            string srcId = dEdgeCount.ToString();
            Node src = g.AddNode(srcId);

            dEdgeCount++;
            string tgtId = dEdgeCount.ToString(); 
            Node tgt = g.AddNode(tgtId);
            src.Attr.Label = i.ToString() + "\n" + i1.ToString();
            tgt.Attr.Label = j.ToString() + "'\n" + j1.ToString() + "'";
            Edge e = g.AddEdge(srcId, tgtId);
            e.SourceNode.Attr.Shape = Microsoft.Glee.Drawing.Shape.Plaintext;
            e.TargetNode.Attr.Shape = Microsoft.Glee.Drawing.Shape.Plaintext;
            e.Attr.Color = Microsoft.Glee.Drawing.Color.Blue;
            e = g.AddEdge(srcId, tgtId);
            e.Attr.Color = Microsoft.Glee.Drawing.Color.Blue;
        }

        private string ComputeEdgeTag(BJK.D.t t)
        {
            string tag;
            if (t.Item == BJK.D.One.Item)
                tag = "1";
            else if (t.Item == BJK.D.Zero.Item)
                tag = "0";
            else if (t.Item == BJK.D.Two.Item)
                tag = "2";
            else if (t.Item == BJK.D.Three.Item)
                tag = "3";
            else
                tag = "1+";
            return tag;
        }
        internal void SetGraph(Dictionary<BIT_ARRAY.Bitarray, Dictionary<BIT_ARRAY.Bitarray, Microsoft.FSharp.Core.FSharpRef<BJK.sop_bar.DFR>>> graph)
        {
            Graph g = new Graph("Context Graph");
            g.GraphAttr.LayerDirection = LayerDirection.LR;
            Tuple<Graph,Graph> lastDfr = null;
            Edge lastEdge = null;
            foreach (CG.edge.E e in CG.edges(graph))
            {
                FSharpList<int> p = CG.contextToVarList(e.Item1);
                FSharpList<int> q = CG.contextToVarList(e.Item2);
                BJK.mat.matrix m = e.Item3.Value.Item1;
                FSharpList<Tuple<Tuple<int,int>,Tuple<int,int>,BJK.Pattern.Pat>> r = e.Item3.Value.Item2;
                Graph dfr = new Graph("DFR");
                foreach (Tuple<int,int,BJK.D.t,BJK.Pattern.Pat> e1 in m.Item)
                {
                    int u = e1.Item1;
                    int v = e1.Item2;
                    string tag = ComputeEdgeTag(e1.Item3);
                    AddVarEdge(dfr, u, v, tag,e1.Item4);
                }
                Graph rg = new Graph("R");
                foreach (Tuple<Tuple<int, int>, Tuple<int,int>,BJK.Pattern.Pat> e1 in r)
                {
                    int i = e1.Item1.Item1;
                    int j = e1.Item1.Item2;
                    int ii = e1.Item2.Item1;
                    int jj = e1.Item2.Item2;
                    AddDoubleVarEdge(rg, i, j, ii, jj);
                }
                Tuple<Graph, Graph> mr = new Tuple<Graph, Graph>(dfr, rg);
                lastEdge = AddEdge(g, p, q, mr);
                lastDfr = mr;
            }
            this.gViewerCG.Graph = g;
            if (lastDfr != null)
            {
                this.gViewerDFR.Graph = lastDfr.Item1;
                this.gViewerR.Graph = lastDfr.Item2;
            }
            else
            {
                this.gViewerDFR.Graph = null;
                this.gViewerR.Graph = null;
            }
            if (lastEdge != null)
            {
                selectedObjectAttr = lastEdge.Attr.Clone();
                lastEdge.Attr.Color = Microsoft.Glee.Drawing.Color.Magenta;
                lastEdge.Attr.Fontcolor = Microsoft.Glee.Drawing.Color.Magenta;
                selectedObject = lastEdge;
            }

            this.tabPage2.Invalidate();
       }

       private  void gViewer_SelectionChanged(object sender, EventArgs e)
       {


           if (gViewerCG.SelectedObject is Edge)
            {
                if (selectedObject != null)
                {
                    if (selectedObject is Edge)
                        (selectedObject as Edge).Attr = selectedObjectAttr as EdgeAttr;
                } 
                selectedObject = gViewerCG.SelectedObject;

                if (selectedObject is Edge)
                {
                    selectedObjectAttr = (gViewerCG.SelectedObject as Edge).Attr.Clone();
                    (gViewerCG.SelectedObject as Edge).Attr.Color = Microsoft.Glee.Drawing.Color.Magenta;
                    (gViewerCG.SelectedObject as Edge).Attr.Fontcolor = Microsoft.Glee.Drawing.Color.Magenta;
                    Edge edge = gViewerCG.SelectedObject as Edge;
                    Tuple<Graph, Graph> dfr = edge.UserData as Tuple<Graph, Graph>;
                    this.gViewerDFR.Graph = dfr.Item1;
                    this.gViewerR.Graph = dfr.Item2;
                    edge.Attr.LabelSize = new Microsoft.Glee.Drawing.Size(10, 10);
                    this.gViewerCG.AutoSize = false;
                }
            }
            gViewerCG.Invalidate();
        }

       object selectedObjectAttr;
       object selectedObject;

       object selectedDFRObjectAttr;
       object selectedDFRObject;

       private void gViewerDFR_SelectionChanged(object sender, EventArgs e)
       {
           if (gViewerDFR.SelectedObject is Edge)
           {
               if (selectedDFRObject != null)
               {
                   if (selectedDFRObject is Edge)
                       (selectedDFRObject as Edge).Attr = selectedDFRObjectAttr as EdgeAttr;
               }
               selectedDFRObject = gViewerDFR.SelectedObject;

               if (selectedDFRObject is Edge)
               {
                   selectedDFRObjectAttr = (gViewerDFR.SelectedObject as Edge).Attr.Clone();
                   (gViewerDFR.SelectedObject as Edge).Attr.Color = Microsoft.Glee.Drawing.Color.Magenta;
                   (gViewerDFR.SelectedObject as Edge).Attr.Fontcolor = Microsoft.Glee.Drawing.Color.Magenta;
                   Edge edge = gViewerDFR.SelectedObject as Edge;
                   string pattern = edge.UserData as string;
                   textBoxPattern.Text = pattern;
                   edge.Attr.LabelSize = new Microsoft.Glee.Drawing.Size(10, 10);
                   this.gViewerDFR.AutoSize = false;
               }
           }
           gViewerDFR.Invalidate();
       }

       private void gViewerDFR_MouseDoubleClick(object sender, MouseEventArgs e)
       {
           Edge edge = gViewerDFR.SelectedObject as Edge;
           if (edge != null)
           {
               string pattern = edge.UserData as string;
               DebugLogForm frm = new DebugLogForm(null, "Computational Path");
               frm.AddText(pattern);
               frm.Show();
           }
       }
    }
}
